from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
import datetime, os
from typing import cast
from config_types import IntermediateCAConfig


def generate_intermediate_ca(config: IntermediateCAConfig, root_path: str = ""):
    """
    生成中间 CA 证书
    
    ⚠️ 重要提示：
    如果重新生成中间CA（使用新的密钥对），需要注意证书链的匹配：
    - Server证书中的AuthorityKeyIdentifier（AKI）指向签发它的中间CA的公钥标识符
    - 在TLS握手时，服务器发送的fullchain中，Server证书的AKI必须与中间CA证书的SKI匹配
    - 如果服务器继续在fullchain中使用旧的中间CA证书，旧的Server证书仍然有效
    - 如果服务器使用新的中间CA证书替换fullchain，则旧的Server证书将失效，需要重新签发
    
    Args:
        config: 配置字典，包含以下键：
            - root_key_path: 根 CA 私钥路径
            - root_crt_path: 根 CA 证书路径
            - root_password: 根 CA 私钥密码
            - output_dir: 输出目录
            - key_path: 私钥文件路径
            - crt_path: 证书文件路径
            - password: 私钥密码
            - country_name: 国家名称
            - state_or_province_name: 省/州名称
            - locality_name: 城市名称
            - organization_name: 组织名称
            - common_name: 通用名称
            - days: 有效期（天数）
            - key_size: RSA密钥长度（可选，默认4096位）
        root_path: 根路径，会与配置中的路径拼接
    """
    ROOT_KEY_PATH = os.path.join(root_path, config["root_key_path"]) if root_path else config["root_key_path"]
    ROOT_CRT_PATH = os.path.join(root_path, config["root_crt_path"]) if root_path else config["root_crt_path"]
    ROOT_PASS = config["root_password"].encode()

    KEY_PATH = os.path.join(root_path, config["key_path"]) if root_path else config["key_path"]
    CRT_PATH = os.path.join(root_path, config["crt_path"]) if root_path else config["crt_path"]
    PASSWORD = config["password"].encode()

    COUNTRY_NAME = config["country_name"]
    STATE_OR_PROVINCE_NAME = config["state_or_province_name"]
    LOCALITY_NAME = config["locality_name"]
    ORGANIZATION_NAME = config["organization_name"]
    COMMON_NAME = config["common_name"]

    DAYS = config["days"]
    KEY_SIZE = config.get("key_size", 4096)  # 默认4096位

    # 确保输出目录存在
    if os.path.dirname(KEY_PATH):
        os.makedirs(os.path.dirname(KEY_PATH), exist_ok=True)
    if os.path.dirname(CRT_PATH):
        os.makedirs(os.path.dirname(CRT_PATH), exist_ok=True)

    # 检查根 CA 文件是否存在
    if not os.path.exists(ROOT_KEY_PATH):
        raise FileNotFoundError(f"根 CA 私钥文件不存在: {ROOT_KEY_PATH}")
    if not os.path.exists(ROOT_CRT_PATH):
        raise FileNotFoundError(f"根 CA 证书文件不存在: {ROOT_CRT_PATH}")

    root_key = serialization.load_pem_private_key(
        open(ROOT_KEY_PATH, "rb").read(), password=ROOT_PASS
    )
    root_cert = x509.load_pem_x509_certificate(open(ROOT_CRT_PATH, "rb").read())
    
    # 类型断言：我们知道这是 RSA 私钥（因为 make_root_ca.py 生成的是 RSA 密钥）
    root_key = cast(rsa.RSAPrivateKey, root_key)

    key = rsa.generate_private_key(public_exponent=65537, key_size=KEY_SIZE)
    subject = x509.Name(
        [
            x509.NameAttribute(NameOID.COUNTRY_NAME, COUNTRY_NAME),
            x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, STATE_OR_PROVINCE_NAME),
            x509.NameAttribute(NameOID.LOCALITY_NAME, LOCALITY_NAME),
            x509.NameAttribute(NameOID.ORGANIZATION_NAME, ORGANIZATION_NAME),
            x509.NameAttribute(NameOID.COMMON_NAME, COMMON_NAME),
        ]
    )
    cert = (
        x509.CertificateBuilder()
        .subject_name(subject)
        .issuer_name(root_cert.subject)
        .public_key(key.public_key())
        .serial_number(x509.random_serial_number())
        .not_valid_before(datetime.datetime.utcnow())
        .not_valid_after(datetime.datetime.utcnow() + datetime.timedelta(days=DAYS))
        # ---- 基础扩展 ----
        .add_extension(x509.BasicConstraints(ca=True, path_length=0), critical=True)
        # ---- 权限声明：允许签发下级证书与 CRL ----
        .add_extension(
            x509.KeyUsage(
                digital_signature=True,  # 允许签名
                content_commitment=False,
                key_encipherment=False,
                data_encipherment=False,
                key_agreement=False,
                key_cert_sign=True,  # 可签发证书
                crl_sign=True,  # 可签发吊销列表
                encipher_only=False,
                decipher_only=False,
            ),
            critical=True,
        )
        # ---- Extended Key Usage：让中间 CA 能签发任意用途证书 ----
        .add_extension(
            x509.ExtendedKeyUsage([ExtendedKeyUsageOID.ANY_EXTENDED_KEY_USAGE]),
            critical=False,
        )
        # ---- 标识符扩展 ----
        # SubjectKeyIdentifier: 本证书的公钥标识符（供下级证书的AKI引用）
        .add_extension(
            x509.SubjectKeyIdentifier.from_public_key(key.public_key()),
            critical=False,
        )
        # AuthorityKeyIdentifier: 标识签发本证书的CA的公钥
        # 从根CA的公钥计算哈希，用于证书链验证时匹配根CA的SKI
        .add_extension(
            x509.AuthorityKeyIdentifier.from_issuer_public_key(
                cast(rsa.RSAPublicKey, root_cert.public_key())
            ),
            critical=False,
        )
        .sign(root_key, hashes.SHA256())
    )

    with open(KEY_PATH, "wb") as f:
        f.write(
            key.private_bytes(
                serialization.Encoding.PEM,
                serialization.PrivateFormat.TraditionalOpenSSL,
                serialization.BestAvailableEncryption(PASSWORD),
            )
        )
    with open(CRT_PATH, "wb") as f:
        f.write(cert.public_bytes(serialization.Encoding.PEM))

    print("Intermediate CA 生成成功：", CRT_PATH)
    return KEY_PATH, CRT_PATH


if __name__ == "__main__":
    # 独立运行时从配置文件加载
    import json
    from config_types import ConfigDict
    config_path = os.path.join(os.path.dirname(__file__), "./configs/test.json")
    with open(config_path, "r", encoding="utf-8") as f:
        full_config = cast(ConfigDict, json.load(f))
        if "intermediate_ca" not in full_config or not full_config["intermediate_ca"]:
            raise ValueError("配置文件中缺少 'intermediate_ca' 字段或列表为空")
        config = full_config["intermediate_ca"][0]
        root_path = full_config.get("root_path", "")
    generate_intermediate_ca(config, root_path)
